﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.Xna.Framework;
using DataTypes;

namespace KinectGame
{
    public enum LevelStatus
    {
        Active, Fail, NoObjects, Finished, InActive, Paused
    }

    public class Level
    {
        private LevelData data;
        private List<GameObject> staticGameObjects = new List<GameObject>();

        public List<GameObject> StaticGameObjects
        {
            get { return staticGameObjects; }
        }
        private List<GameObject> gameObjects = new List<GameObject>();

        public List<GameObject> GameObjects
        {
            get { return gameObjects; }
        }
        private GameObject activeGameObject;

        private List<GameObject> successFailObjects = new List<GameObject>();

        public List<GameObject> SuccessFailObjects
        {
            get { return successFailObjects; }
        }


        private int goCounter = 0;

        public GameObject ActiveGameObject
        {
            get { return activeGameObject; }
        }

        private GameObject ground;
        private LevelStatus status = LevelStatus.InActive;

        public LevelStatus Status
        {
            get { return status; }
            set { status = value; }
        }
        private ObjectCollisionCallback callback;

        private TimeSpan afterNoObjectsTime = new TimeSpan(0, 0, 5);
        private DateTime noObjectsTime;

        private int levelWidth, levelHeight, minHeight, maxHeight, dynamicObjectHeight;

        private LevelTimer timer;

        public LevelTimer Timer
        {
            get { return timer; }
        }

        public Level(LevelData data)
        {
            this.data = data;

            levelWidth = data.x;
            levelHeight = data.y;

            minHeight = data.minHeight;
            maxHeight = data.maxHeight;

            dynamicObjectHeight = data.dynamicObjectHeight;

            ground = new GameObject(Game.models["ground"], new Vector3(50, 1, 50), Vector3.Zero, Vector3.Down * 4, 0.0f);
            staticGameObjects.Add(ground);

            foreach (var go in data.staticGameObjects)
            {
                GameObject staticGameObject = new GameObject(Game.models[go.name], go);
                staticGameObjects.Add(staticGameObject);
            }

            foreach (var go in data.dynamicGameObjects)
            {
                gameObjects.Add(new GameObject(Game.models[go.name], go, dynamicObjectHeight));
            }

            callback = new ObjectCollisionCallback(this);

            timer = new LevelTimer(new TimeSpan(0,0,data.seconds));
            timer.Position = new Vector3(0, 0, -25);
        }

        public BoundingBox GetLevelWorkArea()
        {
            return new BoundingBox(new Vector3(-levelWidth / 2, minHeight, -levelHeight / 2), new Vector3(levelWidth / 2, maxHeight, levelHeight / 2));
        }

        public void ActivateLevel()
        {
            status = LevelStatus.Active;
            foreach (var go in staticGameObjects)
            {
                go.Activate();
                Game.World.AddRigidBody(go.RigidBody);
            }

            Game.Camera.Zoom = 48.0f;
            Game.Camera.VerticalAngle = 1.0f;
            Game.Camera.HorizontalAngle = -0.71f;

            successFailRelease = false;

            //timer.Start();
        }

        public void Update(GameTime gameTime)
        {
            if (status != LevelStatus.Paused)
            {
                if (status == LevelStatus.Active || status == LevelStatus.NoObjects)
                {
                    if (activeGameObject != null && status != LevelStatus.NoObjects)
                    {
                        timer.Update();
                    }
                    Game.World.ContactTest(ground.RigidBody, callback);
                }

                if (status == LevelStatus.NoObjects && DateTime.Now - noObjectsTime > afterNoObjectsTime)
                {
                    status = LevelStatus.Finished;
                    //DeactivateLevel();
                }

                if (status == LevelStatus.Fail && !successFailRelease)
                {
                    ReleaseSuccesFailObjects(true);
                    successFailRelease = true;
                }

                if (status == LevelStatus.Finished && !successFailRelease)
                {
                    ReleaseSuccesFailObjects(false);
                    successFailRelease = true;
                }
            }
        }

        bool successFailRelease = false;

        private void ReleaseSuccesFailObjects(bool isFail)
        {
            /*Vector3 pos = new Vector3(-levelWidth/2, maxHeight, -levelHeight/2);

            int dx = levelWidth / 10;
            int dz = levelWidth / 10;*/

            if (isFail)
            {
                /*for (int i = 0; i < 99; i++)
			    {
                    GameObject go = new GameObject(Game.models["fail"], Vector3.One, Vector3.Zero, pos, 1.0f);
			        successFailObjects.Add(go);
                    go.Activate();
                    Game.World.AddRigidBody(go.RigidBody);

                    pos.X += dx;

                    if (i % 10 == 0)
                    {
                        pos.Z += dz;
                        pos.X = -levelWidth / 2;
                    }
			    }*/

                GameObject bigOne = new GameObject(Game.models["fail"], Vector3.One * 5, Vector3.Zero, Vector3.Up * maxHeight, 100.0f);
                successFailObjects.Add(bigOne);
                bigOne.Activate();
                Game.World.AddRigidBody(bigOne.RigidBody);
            }

            /*dx = levelWidth / 7;
            dz = levelWidth / 7;*/

            if (!isFail)
            {
                /*for (int i = 0; i < 49; i++)
                {
                    GameObject go = new GameObject(Game.models["success"], Vector3.One * 0.3f, Vector3.Zero, pos, 1.0f);
                    successFailObjects.Add(go);
                    go.Activate();
                    Game.World.AddRigidBody(go.RigidBody);

                    pos.X += dx;

                    if (i % 10 == 0)
                    {
                        pos.Z += dz;
                        pos.X = -levelWidth / 2;
                    }
                }*/

                GameObject bigOne = new GameObject(Game.models["success"], Vector3.One * 3, Vector3.Zero, Vector3.Up * maxHeight, 100.0f);
                successFailObjects.Add(bigOne);
                bigOne.Activate();
                Game.World.AddRigidBody(bigOne.RigidBody);
            }
        }

        public void DeactivateLevel()
        {
            foreach (var go in staticGameObjects)
            {
                if (go.RigidBody != null)
                {
                    Game.World.RemoveRigidBody(go.RigidBody);
                }
            }
            foreach (var go in gameObjects)
            {
                if (go.RigidBody != null)
                {
                    Game.World.RemoveRigidBody(go.RigidBody);
                }
            }
            foreach (var go in successFailObjects)
            {
                if (go.RigidBody != null)
                {
                    Game.World.RemoveRigidBody(go.RigidBody);
                }
            }

            timer.Clear();
        }

        public void RestartLevel()
        {
            timer.Clear();
            DeactivateLevel();
            staticGameObjects.Clear();
            gameObjects.Clear();
            successFailObjects.Clear();

            ground = new GameObject(Game.models["ground"], new Vector3(50, 1, 50), Vector3.Zero, Vector3.Down * 4, 0.0f);
            staticGameObjects.Add(ground);

            foreach (var go in data.staticGameObjects)
            {
                GameObject staticGameObject = new GameObject(Game.models[go.name], go);
                staticGameObjects.Add(staticGameObject);
            }

            foreach (var go in data.dynamicGameObjects)
            {
                gameObjects.Add(new GameObject(Game.models[go.name], go, dynamicObjectHeight));
            }

            activeGameObject = null;
            goCounter = 0;
        }

        public GameObject GetActiveGameObject()
        {
            if (status == LevelStatus.Active)
            {
                if (activeGameObject != null)
                {
                    activeGameObject.Activate();
                    Game.World.AddRigidBody(activeGameObject.RigidBody);
                }
                if (goCounter < gameObjects.Count)
                {
                    activeGameObject = gameObjects[goCounter++];
                    activeGameObject.Visible = true;
                    timer.Clear();
                    timer.Start();
                    return activeGameObject;
                }
                else
                {
                    noObjectsTime = DateTime.Now;
                    status = LevelStatus.NoObjects;
                    return null;
                }
            }

            return activeGameObject;
        }

        public GameObject GetNextActiveGameObject()
        {
            if (activeGameObject != null)
            {
                if (goCounter < gameObjects.Count)
                {
                    return gameObjects[goCounter];
                }
                else
                {
                    return null;
                }
            }
            else
            {
                if (gameObjects.Count > 0)
                {
                    return gameObjects[0];
                }
                else
                {
                    return null;
                }
            }
        }
    }
}
